package com.albert.pjsua2;

import android.app.Activity;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;

import com.albert.View.TopActionBarCallback;
import com.albert.View.TopActionBarPopup;
import com.albert.iotcar.CallActivity;
import com.albert.iotcar.R;
import com.albert.tool.StringTool;

import org.pjsip.pjsua2.AccountConfig;
import org.pjsip.pjsua2.AuthCredInfo;
import org.pjsip.pjsua2.AuthCredInfoVector;
import org.pjsip.pjsua2.CallInfo;
import org.pjsip.pjsua2.CallOpParam;
import org.pjsip.pjsua2.OnCallMediaEventParam;
import org.pjsip.pjsua2.StringVector;
import org.pjsip.pjsua2.pjsip_inv_state;
import org.pjsip.pjsua2.pjsip_status_code;

public class SipManager implements Handler.Callback, MyAppObserver {
    public static MyApp app = null;
    public static MyCall currentCall = null;
    public static MyAccount account = null;
    public static AccountConfig accCfg = null;

    private final Handler handler = new Handler(this);

    private Context activityContext;
    private static SipManager instance = new SipManager();

    private SipManagerCallBack callBack;
    private SipManager() { }

    public static SipManager getInstance() {
        return instance;
    }
    public void accountSetting(Context context,
                               String sipAccount,
                               String sipPass,
                               String sipServerIp,
                               int sipServerPort,
                               SipManagerCallBack callBack) {
        NetworkUtils.getInstance().registerNetworkReceiver(context);
        this.activityContext = context;
        this.callBack = callBack;
        if (app == null) {
            app = new MyApp();
            // Wait for GDB to init, for native debugging only
            if (false && (context.getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) {
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                }
            }
            app.init(this, context.getFilesDir().getAbsolutePath());
        }
        accCfg = new AccountConfig();
        accCfg.setIdUri("sip:" + sipAccount + "@" + sipServerPort);
        accCfg.getRegConfig().setRegistrarUri("sip:" + sipAccount + "@" + sipServerIp + ":" + sipServerPort);
        accCfg.getNatConfig().setIceEnabled(true);
        accCfg.getVideoConfig().setAutoTransmitOutgoing(true);
        accCfg.getVideoConfig().setAutoShowIncoming(true);
        account = app.addAcc(accCfg);

        StringVector proxies = accCfg.getSipConfig().getProxies();
        proxies.clear();
        proxies.add("sip:" + sipServerIp);

        AuthCredInfoVector creds = accCfg.getSipConfig().getAuthCreds();
        creds.clear();
        creds.add(new AuthCredInfo("Digest", "*", sipAccount, 0, sipPass));

        try {
            account.modify(accCfg);
        } catch (Exception e) { }
    }

    public void notifyRegState(int code, String reason, long expiration) {
        String msg_str = "";
        if (expiration == 0) msg_str += "Unregistration";
        else msg_str += "Registration";

        if (code / 100 == 2) msg_str += " successful";
        else msg_str += " failed: " + reason;

        Message m = Message.obtain(handler, MSG_TYPE.REG_STATE, msg_str);
        m.sendToTarget();
    }
    public void notifyCallState(MyCall call) {
        if (currentCall == null || call.getId() != currentCall.getId()) return;

        CallInfo ci = null;
        try {
            ci = call.getInfo();
        } catch (Exception e) {
        }

        if (ci == null) return;

        Message m = Message.obtain(handler, MSG_TYPE.CALL_STATE, ci);
        m.sendToTarget();
    }
    /*
     * === MyAppObserver ===
     *
     * As we cannot do UI from worker thread, the callbacks mostly just send
     * a message to UI/main thread.
     */
    public void notifyIncomingCall(MyCall call) {
        Message m = Message.obtain(handler, MSG_TYPE.INCOMING_CALL, call);
        m.sendToTarget();
    }

    @Override
    public boolean handleMessage(Message m) {
        if (m.what == 0) {
            app.deinit();

            if (callBack != null) {
                callBack.finishCallBack();
            }
            Runtime.getRuntime().gc();
            android.os.Process.killProcess(android.os.Process.myPid());
        } else if (m.what == MSG_TYPE.CALL_STATE) {
            CallInfo ci = (CallInfo) m.obj;
            if (currentCall == null || ci == null || ci.getId() != currentCall.getId()) {
                System.out.println("Call state event received, but call info is invalid");
                return true;
            }
            /* Forward the call info to CallActivity */
            if (CallActivity.handler_ != null) {
                Message m2 = Message.obtain(CallActivity.handler_, MSG_TYPE.CALL_STATE, ci);
                m2.sendToTarget();
            }

            if (ci.getState() == pjsip_inv_state.PJSIP_INV_STATE_DISCONNECTED) {
                currentCall.delete();
                currentCall = null;
            }

        } else if (m.what == MSG_TYPE.CALL_MEDIA_STATE) {
            /* Forward the message to CallActivity */
            if (CallActivity.handler_ != null) {
                Message m2 = Message.obtain(CallActivity.handler_, MSG_TYPE.CALL_MEDIA_STATE, null);
                m2.sendToTarget();
            }

        } else if (m.what == MSG_TYPE.BUDDY_STATE) {
            MyBuddy buddy = (MyBuddy) m.obj;
            int idx = account.buddyList.indexOf(buddy);
            if (callBack != null) {
                callBack.BUDDY_STATECallBack(idx, buddy.getStatusText());
                notifyCallState(currentCall);
            }

        } else if (m.what == MSG_TYPE.REG_STATE) {
            if (callBack != null) {
                String msg_str = (String) m.obj;
                callBack.REG_STATECallBack(msg_str);
            }

        } else if (m.what == MSG_TYPE.INCOMING_CALL) {

            /* Incoming call */
            final MyCall call = (MyCall) m.obj;
            CallOpParam prm = new CallOpParam(true);

            /* Only one call at anytime */
            if (currentCall != null) {
                prm.setStatusCode(pjsip_status_code.PJSIP_SC_BUSY_HERE);
                try {
                    call.hangup(prm);
                } catch (Exception e) {
                }

                call.delete();
                return true;
            }

            /* Answer with ringing */
            prm.setStatusCode(pjsip_status_code.PJSIP_SC_RINGING);
            try {
                call.answer(prm);
            } catch (Exception e) {
            }

            currentCall = call;


            new Handler(Looper.getMainLooper()).postDelayed(() -> {
                TopActionBarPopup actionBarPopup = new TopActionBarPopup((Activity)activityContext, ((Activity) activityContext).findViewById(R.id.toolbar), new TopActionBarCallback() {
                    @Override
                    public void pickUpCall() {
                        if (callBack != null) {
                            callBack.INCOMING_CALLCallBack(true);
                        }
                    }
                    @Override
                    public void handUpCall() {

                    }
                });
                actionBarPopup.show();
            }, 100);
        } else if (m.what == MSG_TYPE.CHANGE_NETWORK) {
            app.handleNetworkChange();
        } else {
            /* Message not handled */
            return false;
        }
        return true;
    }

    public void notifyCallMediaState(MyCall call) {
        Message m = Message.obtain(handler, MSG_TYPE.CALL_MEDIA_STATE, null);
        m.sendToTarget();
    }

    public void notifyBuddyState(MyBuddy buddy) {
        Message m = Message.obtain(handler, MSG_TYPE.BUDDY_STATE, buddy);
        m.sendToTarget();
    }

    public void notifyChangeNetwork() {
        Message m = Message.obtain(handler, MSG_TYPE.CHANGE_NETWORK, null);
        m.sendToTarget();
    }

    public void notifyCallMediaEvent(MyCall call, OnCallMediaEventParam prm) {
        /* Forward the message to CallActivity */
        if (CallActivity.handler_ != null) {
            Message m = Message.obtain(CallActivity.handler_, MSG_TYPE.CALL_MEDIA_EVENT, prm);
            m.sendToTarget();
        }
    }

    //拨打电话
    public void makeCall(String uri, SipManagerCallBack callBack) {
        if (StringTool.isBlank(uri)) return;

        /* Only one call at anytime */
        if (currentCall != null) {
            return;
        }

        MyCall call = new MyCall(account, -1);
        CallOpParam prm = new CallOpParam(true);

        try {
            call.makeCall(uri, prm);
        } catch (Exception e) {
            call.delete();
            return;
        }
        currentCall = call;
        if (callBack != null) {
            callBack.INCOMING_CALLCallBack(false);
        }
    }
}
